package com.baizhiedu.jdk;

import com.baizhiedu.proxy.User;
import com.baizhiedu.proxy.UserService;
import com.baizhiedu.proxy.UserServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 功能：
 * 作者：HZj
 * 日期：2024/12/26 22:04
 */
public class TestJDKProxy {
    /*
    *   1.借用类加载器    TestJDKProxy
    *                   UserServiceImpl
    *   2.JDK8.x前
    *       final UserService userService = new UserServiceImpl();
    *       因为JDK8.0之前内部类要访问外部类的局部变量，外部类的局部变量应该声明成final
    *   3.代理类和原始类实现相同接口的原因
    *       1）保证代理类 和 原始类 方法一致 迷惑调用者
    *       2）代理类 可以提供新的实现（额外功能 + 对应原始方法）
    */
    public static void main(String[] args) {
        //1.创建原始对象
        /*因为现在是摒弃Spring框架的情况下，在研究AOP的底层原理，所以此时通过new的方式创建对象*/
        UserService userService = new UserServiceImpl();

        //2.JDK创建动态代理
        /*
        * Proxy.newProxyInstance(classLoader,interfaces,invocationHandler)
        * 代理创建3要素：
        *   1）原始对象
        *   2）额外功能 <--> invocationHandler
        *   invocationHandler接口（实现invoke方法）
        *       invoke方法的作用：
        *           用于书写额外功能，额外功能 运行在原始方法 执行前 后 前后 抛出异常
        *           Object：原始方法的返回值
        *           参数：
        *               Proxy：忽略掉 代表的是代理对象
        *               Method：额外功能 所增加给的那个原始方法
        *               Object[]：原始方法的参数
        *           Object invoke(Object proxy, Method method, Object[] args){
        *               System.out.println("---log--");
        *               Object ret = method.invoke(userService,args);//等价于 userService.login("suns","123456"); 或 userService.register(new User());
        *               return ret;
        *           }
        *   MethodInterceptor接口：
        *       Object invoke(MethodInvocation invocation){
        *           Object ret = invocation.proceed();
        *           return ret;
        *       }
        *   3）代理对象和原始对象实现相同的接口 <--> interfaces
        *   interfaces：原始对象 所实现的接口
        *   userService.getClass().getInterfaces();
        * 类加载器的作用 ClassLoader
        * 1.通过类加载器把对应类的字节码文件加载到JVM
        * 2.通过类加载器创建类的Class对象，进而创建这个类的对象
        * 如何获得类加载器：JVM会为每个类的.class文件 自动分配与之对应的ClassLaoder(即一个.class文件分配一个CL)
        * JVM--->动态代理类--->代理对象
        *           字节码
        *       代理类Class对象（CL）
        *           代理对象
        * 动态代理类并并没有与之对应的源文件(.java)和字节码文件(.class)
        * 动态代理类是通过动态字节码技术 创建字节码 <--> Proxy.newProxyInstance(classloader,interfaces,invocationHandler)
        * 此时在动态代理创建的过程中，需要ClassLoader创建代理类的CLass对象，可是因为动态代理类没有对应的.class文件，
        * JVM也就不会为其分配CLassLoader，但是又需要？  ---》 借用一个CLassLoader
        *
        * 总结：classloader：借用一个类加载器，创建代理类的Class对象，进而可以创建代理对象
        */
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("-----proxy log-----");
                //原始方法运行
                Object ret = method.invoke(userService, args);
                return ret;
            }
        };
        //UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
        userServiceProxy.login("suns","123456");
        userServiceProxy.register(new User());
    }
}
